#include <strtox.h>
#include <ctype.h>
#include <errno.h>

#define STRTOX_OVERFLOW    (1U << 31)

static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
{
    if (*base == 0) {
        if (s[0] == '0') {
            if (tolower(s[1]) == 'x' && isxdigit(s[2]))
                *base = 16;
            else 
                *base = 8;
        } else
            *base = 10;
    }   
    if (*base == 16 && s[0] == '0' && tolower(s[1]) == 'x')
        s += 2;
    return s;
}  

/*          
 * Convert non-negative integer string representation in explicitly given radix
 * to an integer.
 * Return number of characters consumed maybe or-ed with overflow bit.
 * If overflow occurs, result integer (incorrect) is still returned.
 */ 
static unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long *res)
{
    unsigned int rv;
	unsigned long long res1;
    int overflow;
	unsigned int val;

    *res = 0;
    rv = 0;
    overflow = 0;
    while (*s) {
        if ('0' <= *s && *s <= '9')
            val = *s - '0';
        else if ('a' <= tolower(*s) && tolower(*s) <= 'f')
            val = tolower(*s) - 'a' + 10;
        else
            break;

        if (val >= base)
            break;
		res1 = *res;	
        *res = *res * base + val;
		if (*res < res1) 
			overflow = 1;
        rv++;
        s++;
    }
    if (overflow)
        rv |= STRTOX_OVERFLOW;
    return rv;
}

static int _strtoull(const char *s, unsigned int base, unsigned long long *res)
{
    unsigned long long _res;
    unsigned int rv;

    s = _parse_integer_fixup_radix(s, &base);
    rv = _parse_integer(s, base, &_res);
    if (rv & STRTOX_OVERFLOW)
        return -ERANGE;
    rv &= ~STRTOX_OVERFLOW;
    if (rv == 0)
        return -EINVAL;
    s += rv;
    if (*s == '\n')
        s++;
    if (*s)
        return -EINVAL;
    *res = _res;
    return 0;
}

int strtoull(const char *s, unsigned int base, unsigned long long *res)
{
    if (s[0] == '+')
        s++; 
    return _strtoull(s, base, res);
}   

int strtoll(const char *s, unsigned int base, long long *res)
{
    unsigned long long tmp;
    int rv;

    if (s[0] == '-') {
        rv = _strtoull(s + 1, base, &tmp);
        if (rv < 0)
            return -ERANGE;
        if ((long long)(-tmp) >= 0)
            return -ERANGE;
        *res = -tmp;
    } else {
        rv = strtoull(s, base, &tmp);
        if (rv < 0)
            return rv;
        if ((long long)tmp < 0)
            return -ERANGE;
        *res = tmp;
    }
    return 0;
}

int strtoul(const char *s, unsigned int base, unsigned long *res)
{
    unsigned long long tmp;
    int rv;

    rv = strtoull(s, base, &tmp);
    if (rv < 0)
        return rv;
    if (tmp != (unsigned long long)(unsigned long)tmp)
        return -ERANGE;
    *res = tmp;
    return 0;
}

int strtol(const char *s, unsigned int base, long *res)
{
    long long tmp;
    int rv;

    rv = strtoll(s, base, &tmp);
    if (rv < 0)
        return rv;
    if (tmp != (long long)(long)tmp)
        return -ERANGE;
    *res = tmp;
    return 0;
}

int strtouint(const char *s, unsigned int base, unsigned int *res)
{
    unsigned long long tmp;
    int rv;

    rv = strtoull(s, base, &tmp);
    if (rv < 0)
        return rv;
    if (tmp != (unsigned long long)(unsigned int)tmp)
        return -ERANGE;
    *res = tmp;
    return 0;
}

int strtoint(const char *s, unsigned int base, int *res)
{
    long long tmp;
    int rv;

    rv = strtoll(s, base, &tmp);
    if (rv < 0)
        return rv;
    if (tmp != (long long)(int)tmp)
        return -ERANGE;
    *res = tmp;
    return 0;
}

int strtou16(const char *s, unsigned int base, u16 *res)
{
    unsigned long long tmp;
    int rv;

    rv = strtoull(s, base, &tmp);
    if (rv < 0)
        return rv;
    if (tmp != (unsigned long long)(u16)tmp)
        return -ERANGE;
    *res = tmp;
    return 0;
}

int strtos16(const char *s, unsigned int base, s16 *res)
{
    long long tmp;
    int rv;

    rv = strtoll(s, base, &tmp);
    if (rv < 0)
        return rv;
    if (tmp != (long long)(s16)tmp)
        return -ERANGE;
    *res = tmp;
    return 0;
}

int strtou8(const char *s, unsigned int base, u8 *res)
{
    unsigned long long tmp;
    int rv;

    rv = strtoull(s, base, &tmp);
    if (rv < 0)
        return rv;
    if (tmp != (unsigned long long)(u8)tmp)
        return -ERANGE;
    *res = tmp;
    return 0;
}

int strtos8(const char *s, unsigned int base, s8 *res)
{
    long long tmp;
    int rv;

    rv = strtoll(s, base, &tmp);
    if (rv < 0)
        return rv;
    if (tmp != (long long)(s8)tmp)
        return -ERANGE;
    *res = tmp;
    return 0;
}

